import { Vnode } from "./vnode"



const createRenderer = () => {

  const unmount = (vnode:Vnode) => {
    const p = vnode.el.parentNode
    p && p.removeChild(vnode.el)
  }

  const setElementText = (el: HTMLElement, text) => {
    el.innerHTML = text
  }

  const insert = (el: HTMLElement, parent, anchor = null) => {
    parent.insertBefore(el, anchor)
    // el newNode 插入的新元素
    // anchor 插入元素的位置  可以是null  新元素就会插入到末尾
    // 如果指定了anchor 就会插入到anchor元素前面
    // 很常用 好用 diff算法也会用到
  }

  const createElement = (tag) => {
    return document.createElement(tag)
  }

  const mountElement = (vnode: Vnode, container) => {
    const root = createElement(vnode.tag)
    vnode.el = root //挂载的时候顺便把真实的dom添加上去 添加上去就能复用了
    if(typeof vnode.children === 'string') {
      setElementText(root, vnode.children)
    } else if(Array.isArray(vnode.children)) {
      vnode.children.forEach(child => {
        patch(null, child, root)
      })
    }


    insert(root, container)
  }

  const patchElement = (n1, n2) => {
    const el = n2.el = n1.el  //复制一份element给新的vnode
    // patchChildren(n1, n2, el)
    patchKeyChildren(n1, n2, el)
  }

  const patchChildren = (n1: Vnode, n2: Vnode, container) => {
    n2.el = n1.el;
    if(typeof n2.children === 'string') {
      // 内容便了
      setElementText(container, n2.children)
    } else if(Array.isArray(n2.children)) {
      // 新增或者删除了
      if(Array.isArray(n1.children)) {
        n1.children.forEach(child => unmount(child))
        n2.children.forEach(child => patch(null, child, container))
      }else {
        n2.children.forEach(child => patch(null, child, container))
      }
    }
  }

  const isSameVnodeType = (n1,n2) => {
    return n1.key === n2.key
  }

  const patchKeyChildren = (n1: Vnode, n2: Vnode, container) => {

    let j = 0; //指针
    const oldChildren = n1.children as Vnode[]//旧的Vnode集合
    const newChildren = n2.children as Vnode[]//新的Vnode集合
    let e1 = oldChildren.length - 1;  //获取旧length
    let e2 = newChildren.length - 1;  //获取新length

    while(j <= e1 && j <= e2) {
      const oldVnode = oldChildren[j];
      const newVnode = newChildren[j];
      if(isSameVnodeType(oldVnode, newVnode)) {
        patch(oldVnode, newVnode, container)
      }else {
        break;
      }
      j++
    }

    while(j <= e1 && j <= e2) {
      const oldVnode = oldChildren[e1];
      const newVnode = newChildren[e2];
      if(isSameVnodeType(oldVnode, newVnode)) {
        patch(oldVnode, newVnode, container)
      }else {
        break;
      }
      e1--
      e2--
    }

  }
  
  /**
   * 
   * @param n1 旧的虚拟dom
   * @param n2 新的虚拟dom
   * @param container 挂载点
   */
  const patch = (n1, n2, container) => {
    if(!n1) {
      // 挂载
      mountElement(n2, container)
    } else{
      // 更新 如果是string,替换文字
      if(typeof n2.children === 'string') {
        patchChildren(n1, n2, n1.el)
      } else {
        // 否则调用这个
        patchElement(n1, n2)
      }
    }
  }

  const render = (vnode: Vnode, container) => {

    if(vnode) {
      patch(container._vnode, vnode, container)
    } else {
      // 不存在
      if(container._vnode) {
        unmount(container._vnode)
      }

    }
    container._vnode = vnode //挂载旧的虚拟dom

  }
  
  return {
    render
  }
}

export const createApp = (vnode) => {

  const renderer = createRenderer()
  
  return {
    mount(container) {
      renderer.render(vnode, container)
    }
  }

}